﻿/*
 * Crée par SharpDevelop.
 * Utilisateur: eldunkerquois
 * Date: 18/04/2011
 * Heure: 22:30
 * 
 * Pour changer ce modèle utiliser Outils | Options | Codage | Editer les en-têtes standards.
 */
using System;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Linq;

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.CustomXmlDataProperties;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using openxml.butter.utils;

namespace openxml.butter.definition
{
	/// <summary>
	/// Modèle de Document, associé à des DataTemplates
	/// </summary>
	[Serializable]
	public class DocumentTemplate : AbstractTemplateDefinition
    {
        private static String _virginPath;

        #region static members
        /// <summary>
        /// INdique le chemin du fichier docx vierge pour la création de modèle
        /// </summary>
        public static String VirginPath
        {
            set { _virginPath = value; }
        }

        /// <summary>
        /// Créer le modèle à partir du bianire du vierge
        /// </summary>
        /// <param name="code"></param>
        /// <param name="description"></param>
        /// <returns></returns>
        public static DocumentTemplate CreateTemplate(String code , String description)
        {
            //création classique
            DocumentTemplate doc = new DocumentTemplate(code, description);
            System.Diagnostics.Trace.WriteLine("modèle parte 1 : " + _virginPath );

            //ouvrir le fichier vierge
            FileStream fs = new FileStream(_virginPath, FileMode.Open, FileAccess.Read );
            byte[] arrBytes = new byte[fs.Length];
            System.Diagnostics.Trace.WriteLine("lecture du fichier vierge");
            //lire le contenu binaire et fermer
            fs.Read(arrBytes, 0,(int) fs.Length);
            fs.Close();
            System.Diagnostics.Trace.WriteLine("taille du vierge =" + arrBytes.Length);
            //fournir le binaire
            doc.BinaryContent = arrBytes;
            return doc;

        }
        #endregion

        #region public properties
       	
		
		/// <summary>
		/// Contenu binaire du modèle
		/// </summary>
		public byte[] BinaryContent { get ; set; }
		

		
	
		
		#endregion
		
		#region Constructeurs
		public DocumentTemplate()
		{			
			
		}
		
		public DocumentTemplate( String code , String description ):base(code, description)
		{
			
		}
		
		
		#endregion
		
		#region Public Methodes
//		/// <summary>
//		/// Récupérer une association donnée - content control par l'id du control
//		/// </summary>
//		/// <param name="controlId"></param>
//		/// <returns></returns>
//		public DataControlAssociation GetDCAByControlId( String controlId )
//		{
//			var query =this.DataControlAssociations.Where( p => p.ControlId == controlId );
//			if ( query.Count<DataControlAssociation>() == 0)
//			{
//				return null;
//			}
//			else
//			{
//				return query.FirstOrDefault<DataControlAssociation>();
//			}
//			
//		}
	
		/// <summary>
		/// Mettre à jour les customsParts à partir des data template
		/// hashMembers : hash d'instances de DataTemplate, clé = code du DataTemplate
		/// hashDataItems : hash de liste de DataItem , clé = code du DataTemplate
		/// </summary>
		public void setCustomParts(Hashtable hashMembers , Hashtable hashDataItems, Hashtable hashControlAssos)
		{
			//récupérer le document .docx lui même
            System.IO.MemoryStream MS = new MemoryStream( this.BinaryContent ) ;

			WordprocessingDocument doc = WordprocessingDocument.Open(MS ,true);
			try
			{
				MainDocumentPart mainPart = doc.MainDocumentPart;
				List<String> toRemove = new List<String>();
				
				//analyser les parts de données pour récupérer les parts inutiles
				foreach (CustomXmlPart cPart1 in mainPart.GetPartsOfType<CustomXmlPart>())
				{
					Console.WriteLine("cPart " + mainPart.GetIdOfPart(cPart1 ) + "; " + cPart1.Uri + " - " + cPart1.CustomXmlPropertiesPart.DataStoreItem.ItemId ) ;
					
					//chercher si existe sinon adieu
					if ( !hashMembers.ContainsKey( cPart1.CustomXmlPropertiesPart.DataStoreItem.ItemId ) )
					{
						String idVire = mainPart.GetIdOfPart( cPart1 ) ;
						toRemove.Add( idVire ) ;
					}
				}
				
				//supprimer les parts inutiles énumérés au dessus
				foreach (String key in toRemove)
				{
					mainPart.DeletePart( key ) ;
					Console.WriteLine( "Suppression du part {0}", key ) ;
				}
				
				
				
				//un fichier customPart.xml par datatemplate
				foreach( DataTemplate dt in hashMembers.Values )
				{					
					
					String partId =dt.Code ;
					
					//recherche la part dans le docx
					CustomXmlPart cPart = GetPart( doc, partId ) ;
					
					//n'existe pas => créer
					if ( cPart == null )
					{
						cPart = mainPart.AddCustomXmlPart(CustomXmlPartType.CustomXml,partId);
						mainPart.CreateRelationshipToPart( cPart , partId ) ;
						//relation avec la part
						Console.WriteLine("création part " + cPart.Uri ) ;
						
						CustomXmlPropertiesPart propsPart = cPart.AddNewPart<CustomXmlPropertiesPart>("main") ;
						
						//l'id du datastore est celui du datatemplate;
						DataStoreItem ds = new DataStoreItem(){ ItemId = partId };
						ds.AddNamespaceDeclaration("ds", "http://schemas.openxmlformats.org/officeDocument/2006/customXml");
						SchemaReferences schemaReferences1 = new SchemaReferences();

						ds.Append(schemaReferences1);
						propsPart.DataStoreItem = ds;
						
					}
					else
					{
						Console.WriteLine("PArt existe : " + cPart.Uri ) ;
					}
					
					//remplir la part
					Stream partStream = cPart.GetStream(FileMode.OpenOrCreate ) ;
					
					//mettre la taille à 0 pour efacer les données antérieures
					partStream.SetLength(0);
					StreamWriter sw = new StreamWriter(partStream) ;
					
					//charger les items du datatemplate
					List<DataItem> items = (List<DataItem>) hashDataItems[dt.Code] ;
					dt.ToStream( sw , items ) ;
					
					sw.Close() ;
					partStream.Close() ;
					
				}
				
				//content control du truc
				foreach( OpenXmlElement cc in ContentControlExtensions.ContentControls( doc ))
				{
					SdtProperties props = cc.Elements<SdtProperties>().FirstOrDefault();
					SdtContentRun content = cc.Elements<SdtContentRun>().FirstOrDefault();
					DataBinding db = props.Elements<DataBinding>().FirstOrDefault();
					SdtAlias alias = props.Elements<SdtAlias>().FirstOrDefault();
					Tag tag = props.Elements<Tag>().FirstOrDefault();
					
					String controlId = tag.Val ;
					
					//TODO chercher l'association
					DataControlAssociation dca = null;
					//DataControlAssociation dca = GetDCAByControlId( controlId ) ;
					if ( hashControlAssos.ContainsKey( controlId ) )
					{
						dca = (DataControlAssociation) hashControlAssos[controlId] ;
					}
					
					
					//control non lié, le lier
					if ( dca== null )
					{
//						db = new DataBinding(){ XPath = "/client/prenom" , StoreItemId= cPart.CustomXmlPropertiesPart.DataStoreItem.ItemId };
//						Console.WriteLine("db storeId ="+cPart.CustomXmlPropertiesPart.DataStoreItem.ItemId ) ;
//						props.Append(db);
						Console.WriteLine( "Content Control {0} associé avec rien!",controlId ) ;
					}
					//control déjà lié, mettre à jour la liaison
					else
					{
						Console.WriteLine("Content Control {0} associé avec {1}!", controlId,dca.DataItem.Code ) ;
						//ménage
						if ( db != null )
						{	Console.WriteLine("remove db:" + db.XPath ) ;
							props.RemoveChild<DataBinding>( db );
						}
						
						//créer dans les 2 cas
						db = new DataBinding(){ XPath = dca.DataItem.FullPath(), StoreItemId=dca.DataItem.DataTemplate.Code };
						//Console.WriteLine("db storeId ="+cPart.CustomXmlPropertiesPart.DataStoreItem.ItemId ) ;
						props.Append(db);
						//db.StoreItemId = cPart.CustomXmlPropertiesPart.DataStoreItem.ItemId;
						//db.XPath = "/client/prenom" ;
						Console.WriteLine("création db {0} , XPath : {1}", db.StoreItemId,db.XPath ) ;
					}
					
					//mettre à jour du contenu
					if ( content == null )
					{
						content = new SdtContentRun();
						cc.Append( content );
					}
					else
					{
						content.RemoveAllChildren();
					}
					
					Run run = new Run();
					Text txt = null ;
					if ( dca == null )
					{
						txt = new Text("!!Unknown!!");
					}
					else
					{
						txt = new Text( dca.DataItem.Value );
					}
					
					run.Append( txt );
					content.Append( run );
					
				}
				
			}
			catch (Exception e )
			{
				Console.WriteLine("toto : " + e.Message ) ;
			}
			finally
			{
				doc.Close();
                MS.Close();
			}
			
		}
		
		
		#endregion
		
		#region Utiles
		private CustomXmlPart GetPart( WordprocessingDocument doc , String partId )
		{
			
			CustomXmlPart res = null ;
			try
			{
				foreach (CustomXmlPart cPart in doc.MainDocumentPart.GetPartsOfType<CustomXmlPart>())
				{
					if ( partId.Equals( doc.MainDocumentPart.GetIdOfPart( cPart ) ) )
					{
						res= cPart ;
						break;
					}
				}
				
			}
			catch ( ArgumentOutOfRangeException )
			{
				res = null;
				Console.WriteLine("Part " + partId + " n'existe pas " );
			}
			
			return res ;
		}
		#endregion
		
		
	}
}
